Happy Moments

Arpita Shah and Tian Zheng

HappyDB is a corpus of 100,000 crowd-sourced happy moments via Amazon’s Mechanical Turk. You can read more about it on https://arxiv.org/abs/1801.07746.

Here, we explore this data set and try to answer the question, “What makes people happy?”

Step 0 - Load all the required libraries

From the packages’ descriptions:


library(tidyverse)
library(tidytext)
library(DT)
library(scales)
library(wordcloud2)
library(gridExtra)
library(ngram)
library(shiny) 

Step 1 - Load the processed text data along with demographic information on contributors

We use the processed data for our analysis and combine it with the demographic information available.

hm_data <- read_csv("../output/processed_moments.csv")

urlfile<-"../data/demographic.csv"
demo_data <- read_csv(urlfile)                                                                

Combine both the data sets and keep the required columns for analysis

We select a subset of the data that satisfies specific row conditions.

hm_data <- hm_data %>%
  inner_join(demo_data, by = "wid") %>%
  select(wid,
         original_hm,
         gender, 
         marital, 
         parenthood,
         reflection_period,
         age, 
         country, 
         ground_truth_category, 
         text) %>%
  mutate(count = sapply(hm_data$text, wordcount)) %>%
  filter(gender %in% c("m", "f")) %>%
  filter(marital %in% c("single", "married")) %>%
  filter(parenthood %in% c("n", "y")) %>%
  filter(reflection_period %in% c("24h", "3m")) %>%
  mutate(reflection_period = fct_recode(reflection_period, 
                                        months_3 = "3m", hours_24 = "24h"))
datatable(hm_data)
Warning: It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.htmlWarning: It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.html

Create a bag of words using the text data

bag_of_words <-  hm_data %>%
  unnest_tokens(word, text)

word_count <- bag_of_words %>%
  count(word, sort = TRUE)

Create bigrams using the text data

hm_bigrams <- hm_data %>%
  filter(count != 1) %>%
  unnest_tokens(bigram, text, token = "ngrams", n = 2)

bigram_counts <- hm_bigrams %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>%
  count(word1, word2, sort = TRUE)

Specify the user interface for the R Shiny app

We want each tab to have its own controls for input and so Shiny’s “navbarPage()” layout works the best. We have the first tab visualizing the overall data, second one for scatterplots comparing the proportion of words within categories, third one highlighting the most frequently appearing bigrams based on categories and the last tab to explore the actual happy moments.

ui <- navbarPage("What makes people happy?",
                 tabPanel("Overview",
                          
                          titlePanel(h1("Most Frequent Occurrences",
                                        align = "center")),
                          
                          sidebarLayout(
                            sidebarPanel(
                              sliderInput(inputId = "topWordcloud",
                                          label = "Number of terms for word cloud:",
                                          min = 5,
                                          max = 100,
                                          value = 50),
                              br(),
                              br(),
                              
                              checkboxInput(inputId = "topFreqB",
                                            label = "Plot Bar Chart",
                                            value = F),
                              sliderInput(inputId = "topBarchart",
                                          label = "Number of terms for bar chart:",
                                          min = 1,
                                          max = 25,
                                          value = 10),
                              br(),
                              br(),
                              
                              checkboxInput(inputId = "topFreqN",
                                            label = "Plot Network Graph",
                                            value = F),
                              sliderInput(inputId = "topNetwork",
                                          label = "Number of edges for network graph:",
                                          min = 1,
                                          max = 150,
                                          value = 50)
                            ),
                            
                            mainPanel(
                              wordcloud2Output(outputId = "WC"),
                              plotOutput(outputId = "figure")
                            )
                          )
                 ),
                 
                 tabPanel("Individual Terms",
                          
                          titlePanel(h1("Comparison of Proportions",
                                        align = "center")),
                          
                          sidebarLayout(
                            sidebarPanel(
                              selectInput(inputId = "attribute",
                                          label = "Select the attribute:",
                                          choices = c("Gender" = "gender",
                                                      "Marital Status" = "marital",
                                                      "Parenthood" = "parenthood",
                                                      "Reflection Period" = "reflection_period")
                              )
                            ),
                            
                            mainPanel(
                              plotOutput(outputId = "scatter")
                            )
                          )
                 ),
                 
                 tabPanel("Pair of Words",
                          
                          titlePanel(h1("Most Frequent Bigrams",
                                        align = "center")),
                          
                          sidebarLayout(
                            sidebarPanel(
                              selectInput(inputId = "factor",
                                          label = "Select the attribute:",
                                          choices = c("Gender" = "gender",
                                                      "Marital Status" = "marital",
                                                      "Parenthood" = "parenthood",
                                                      "Reflection Period" = "reflection_period")
                              ),
                              numericInput(inputId = "topBigrams",
                                          label = "Number of top pairs to view:",
                                          min = 1,
                                          max = 25,
                                          value = 10)
                            ),
                            
                            mainPanel(
                              plotOutput(outputId = "bar")
                            )
                          )
                 ),
                 
                 tabPanel("Data",
                          DT::dataTableOutput("table")
                          )
)

Develop the server for the R Shiny app

This shiny app visualizes summary of data and displays the data table itself.

server <- function(input, output, session) {
  
  pt1 <- reactive({
    if(!input$topFreqB) return(NULL)
    word_count %>%
      slice(1:input$topBarchart) %>%
      mutate(word = reorder(word, n)) %>%
      ggplot(aes(word, n)) +
      geom_col() +
      xlab(NULL) +
      ylab("Word Frequency")+
      coord_flip()
  })
  
  pt2 <- reactive({
    if(!input$topFreqN) return(NULL)
    bigram_graph <- bigram_counts %>%
      slice(1:input$topNetwork) %>%
      graph_from_data_frame()
    
    set.seed(123)
    
    x <- grid::arrow(type = "closed", length = unit(.1, "inches"))
    
    ggraph(bigram_graph, layout = "fr") +
      geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                     arrow = x, end_cap = circle(.05, 'inches')) +
      geom_node_point(color = "skyblue", size = 3) +
      geom_node_text(aes(label = name), repel = TRUE) +
      theme_void()
  })
  
  output$WC <- renderWordcloud2({
    
    word_count %>%
      slice(1:input$topWordcloud) %>%
      wordcloud2(size = 0.6,
                 rotateRatio = 0)
    
  })
  
  output$figure <- renderPlot(height = 500, width = 500, {
    
    ptlist <- list(pt1(),pt2())
    ptlist <- ptlist[!sapply(ptlist, is.null)]
    if(length(ptlist)==0) return(NULL)
    
    lay <- rbind(c(1,1),
                 c(2,2))
    
    grid.arrange(grobs = ptlist, layout_matrix = lay)
  })
  
  
  
  selectedAttribute <- reactive({
    list(atr = input$attribute)
  })
  
  output$scatter <- renderPlot({
    temp <- bag_of_words %>%
      count(!!as.name(selectedAttribute()$atr), word) %>%
      group_by(!!as.name(selectedAttribute()$atr)) %>%
      mutate(proportion = n / sum(n)) %>% 
      select(-n) %>% 
      spread(!!as.name(selectedAttribute()$atr), proportion)
    
      ggplot(temp, 
             aes_string(x = colnames(temp)[2], y = colnames(temp)[3]),
             color = abs(colnames(temp)[3] - colnames(temp)[2])) +
      geom_abline(color = "gray40", lty = 2) +
      geom_jitter(alpha = 0.1, size = 1, width = 0.3, height = 0.3) +
      geom_text(aes(label = word), check_overlap = TRUE, vjust = 1.5) +
      scale_x_log10(labels = percent_format()) +
      scale_y_log10(labels = percent_format()) +
      scale_color_gradient(limits = c(0, 0.001), low = "darkslategray4", high = "gray75") +
      theme(legend.position="none")
  })
  
  
  
  selectedBigram <- reactive({
    list(var = input$factor)
  })
  
  output$bar <- renderPlot({
    hm_bigrams %>%
      count(!!as.name(selectedBigram()$var), bigram, sort = TRUE) %>%
      group_by(!!as.name(selectedBigram()$var)) %>%
      top_n(input$topBigrams) %>%
      ungroup() %>%
      mutate(bigram = reorder(bigram, n)) %>%
      ggplot(aes(bigram, n, fill = !!as.name(selectedBigram()$var))) +
      geom_col(show.legend = FALSE) +
      facet_wrap(as.formula(paste("~", selectedBigram()$var)), ncol = 2, scales = "free") +
      coord_flip()
  })
  
  
  output$table <- DT::renderDataTable({
    DT::datatable(hm_data)
  })
}

Run the R Shiny app

shinyApp(ui, server)
NA
LS0tDQp0aXRsZTogIkhhcHB5IE1vbWVudHMiDQphdXRob3I6ICJBcnBpdGEgU2hhaCBhbmQgVGlhbiBaaGVuZyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KcnVudGltZTogc2hpbnkNCi0tLQ0KDQpIYXBweURCIGlzIGEgY29ycHVzIG9mIDEwMCwwMDAgY3Jvd2Qtc291cmNlZCBoYXBweSBtb21lbnRzIHZpYSBBbWF6b24ncyBNZWNoYW5pY2FsIFR1cmsuIFlvdSBjYW4gcmVhZCBtb3JlIGFib3V0IGl0IG9uIGh0dHBzOi8vYXJ4aXYub3JnL2Ficy8xODAxLjA3NzQ2Lg0KDQpIZXJlLCB3ZSBleHBsb3JlIHRoaXMgZGF0YSBzZXQgYW5kIHRyeSB0byBhbnN3ZXIgdGhlIHF1ZXN0aW9uLCAiV2hhdCBtYWtlcyBwZW9wbGUgaGFwcHk/Ig0KDQojIyMgU3RlcCAwIC0gTG9hZCBhbGwgdGhlIHJlcXVpcmVkIGxpYnJhcmllcw0KDQpGcm9tIHRoZSBwYWNrYWdlcycgZGVzY3JpcHRpb25zOg0KDQorIGB0aWR5dmVyc2VgIGlzIGFuIG9waW5pb25hdGVkIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyBkZXNpZ25lZCBmb3IgZGF0YSBzY2llbmNlLiBBbGwgcGFja2FnZXMgc2hhcmUgYW4gdW5kZXJseWluZyBkZXNpZ24gcGhpbG9zb3BoeSwgZ3JhbW1hciwgYW5kIGRhdGEgc3RydWN0dXJlczsNCisgYHRpZHl0ZXh0YCBhbGxvd3MgdGV4dCBtaW5pbmcgdXNpbmcgJ2RwbHlyJywgJ2dncGxvdDInLCBhbmQgb3RoZXIgdGlkeSB0b29sczsNCisgYERUYCBwcm92aWRlcyBhbiBSIGludGVyZmFjZSB0byB0aGUgSmF2YVNjcmlwdCBsaWJyYXJ5IERhdGFUYWJsZXM7DQorIGBzY2FsZXNgIG1hcCBkYXRhIHRvIGFlc3RoZXRpY3MsIGFuZCBwcm92aWRlIG1ldGhvZHMgZm9yIGF1dG9tYXRpY2FsbHkgZGV0ZXJtaW5pbmcgYnJlYWtzIGFuZCBsYWJlbHMgZm9yIGF4ZXMgYW5kIGxlZ2VuZHM7DQorIGB3b3JkY2xvdWQyYCBwcm92aWRlcyBhbiBIVE1MNSBpbnRlcmZhY2UgdG8gd29yZGNsb3VkIGZvciBkYXRhIHZpc3VhbGl6YXRpb247DQorIGBncmlkRXh0cmFgIGNvbnRhaW5zIG1pc2NlbGxhbmVvdXMgZnVuY3Rpb25zIGZvciAiZ3JpZCIgZ3JhcGhpY3M7DQorIGBuZ3JhbWAgaXMgZm9yIGNvbnN0cnVjdGluZyBuLWdyYW1zICjigJx0b2tlbml6aW5n4oCdKSwgYXMgd2VsbCBhcyBnZW5lcmF0aW5nIG5ldyB0ZXh0IGJhc2VkIG9uIHRoZSBuLWdyYW0gc3RydWN0dXJlIG9mIGEgZ2l2ZW4gdGV4dCBpbnB1dCAo4oCcYmFiYmxpbmfigJ0pOw0KKyBgU2hpbnlgIGlzIGFuIFIgcGFja2FnZSB0aGF0IG1ha2VzIGl0IGVhc3kgdG8gYnVpbGQgaW50ZXJhY3RpdmUgd2ViIGFwcHMgc3RyYWlnaHQgZnJvbSBSOw0KDQpgYGB7ciBsb2FkIGxpYnJhcmllcywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShEVCkNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeSh3b3JkY2xvdWQyKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KG5ncmFtKQ0KbGlicmFyeShzaGlueSkgDQpgYGANCg0KIyMjIFN0ZXAgMSAtIExvYWQgdGhlIHByb2Nlc3NlZCB0ZXh0IGRhdGEgYWxvbmcgd2l0aCBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiBvbiBjb250cmlidXRvcnMNCg0KV2UgdXNlIHRoZSBwcm9jZXNzZWQgZGF0YSBmb3Igb3VyIGFuYWx5c2lzIGFuZCBjb21iaW5lIGl0IHdpdGggdGhlIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uIGF2YWlsYWJsZS4NCg0KYGBge3IgbG9hZCBkYXRhLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KaG1fZGF0YSA8LSByZWFkX2NzdigiLi4vb3V0cHV0L3Byb2Nlc3NlZF9tb21lbnRzLmNzdiIpDQoNCnVybGZpbGU8LSIuLi9kYXRhL2RlbW9ncmFwaGljLmNzdiINCmRlbW9fZGF0YSA8LSByZWFkX2Nzdih1cmxmaWxlKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQojIyMgQ29tYmluZSBib3RoIHRoZSBkYXRhIHNldHMgYW5kIGtlZXAgdGhlIHJlcXVpcmVkIGNvbHVtbnMgZm9yIGFuYWx5c2lzDQoNCldlIHNlbGVjdCBhIHN1YnNldCBvZiB0aGUgZGF0YSB0aGF0IHNhdGlzZmllcyBzcGVjaWZpYyByb3cgY29uZGl0aW9ucy4NCg0KYGBge3IgY29tYmluaW5nIGRhdGEsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpobV9kYXRhIDwtIGhtX2RhdGEgJT4lDQogIGlubmVyX2pvaW4oZGVtb19kYXRhLCBieSA9ICJ3aWQiKSAlPiUNCiAgc2VsZWN0KHdpZCwNCiAgICAgICAgIG9yaWdpbmFsX2htLA0KICAgICAgICAgZ2VuZGVyLCANCiAgICAgICAgIG1hcml0YWwsIA0KICAgICAgICAgcGFyZW50aG9vZCwNCiAgICAgICAgIHJlZmxlY3Rpb25fcGVyaW9kLA0KICAgICAgICAgYWdlLCANCiAgICAgICAgIGNvdW50cnksIA0KICAgICAgICAgZ3JvdW5kX3RydXRoX2NhdGVnb3J5LCANCiAgICAgICAgIHRleHQpICU+JQ0KICBtdXRhdGUoY291bnQgPSBzYXBwbHkoaG1fZGF0YSR0ZXh0LCB3b3JkY291bnQpKSAlPiUNCiAgZmlsdGVyKGdlbmRlciAlaW4lIGMoIm0iLCAiZiIpKSAlPiUNCiAgZmlsdGVyKG1hcml0YWwgJWluJSBjKCJzaW5nbGUiLCAibWFycmllZCIpKSAlPiUNCiAgZmlsdGVyKHBhcmVudGhvb2QgJWluJSBjKCJuIiwgInkiKSkgJT4lDQogIGZpbHRlcihyZWZsZWN0aW9uX3BlcmlvZCAlaW4lIGMoIjI0aCIsICIzbSIpKSAlPiUNCiAgbXV0YXRlKHJlZmxlY3Rpb25fcGVyaW9kID0gZmN0X3JlY29kZShyZWZsZWN0aW9uX3BlcmlvZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGhzXzMgPSAiM20iLCBob3Vyc18yNCA9ICIyNGgiKSkNCmBgYA0KDQpgYGB7cn0NCmRhdGF0YWJsZShobV9kYXRhKQ0KYGBgDQojIyMgQ3JlYXRlIGEgYmFnIG9mIHdvcmRzIHVzaW5nIHRoZSB0ZXh0IGRhdGENCg0KYGBge3IgYmFnIG9mIHdvcmRzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KYmFnX29mX3dvcmRzIDwtICBobV9kYXRhICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpDQoNCndvcmRfY291bnQgPC0gYmFnX29mX3dvcmRzICU+JQ0KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQojIyMgQ3JlYXRlIGJpZ3JhbXMgdXNpbmcgdGhlIHRleHQgZGF0YQ0KDQpgYGB7ciBiaWdyYW0sIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpobV9iaWdyYW1zIDwtIGhtX2RhdGEgJT4lDQogIGZpbHRlcihjb3VudCAhPSAxKSAlPiUNCiAgdW5uZXN0X3Rva2VucyhiaWdyYW0sIHRleHQsIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKQ0KDQpiaWdyYW1fY291bnRzIDwtIGhtX2JpZ3JhbXMgJT4lDQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUNCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQojIyMgU3BlY2lmeSB0aGUgdXNlciBpbnRlcmZhY2UgZm9yIHRoZSBSIFNoaW55IGFwcA0KDQpXZSB3YW50IGVhY2ggdGFiIHRvIGhhdmUgaXRzIG93biBjb250cm9scyBmb3IgaW5wdXQgYW5kIHNvIFNoaW55J3MgIm5hdmJhclBhZ2UoKSIgbGF5b3V0IHdvcmtzIHRoZSBiZXN0LiBXZSBoYXZlIHRoZSBmaXJzdCB0YWIgdmlzdWFsaXppbmcgdGhlIG92ZXJhbGwgZGF0YSwgc2Vjb25kIG9uZSBmb3Igc2NhdHRlcnBsb3RzIGNvbXBhcmluZyB0aGUgcHJvcG9ydGlvbiBvZiB3b3JkcyB3aXRoaW4gY2F0ZWdvcmllcywgdGhpcmQgb25lIGhpZ2hsaWdodGluZyB0aGUgbW9zdCBmcmVxdWVudGx5IGFwcGVhcmluZyBiaWdyYW1zIGJhc2VkIG9uIGNhdGVnb3JpZXMgYW5kIHRoZSBsYXN0IHRhYiB0byBleHBsb3JlIHRoZSBhY3R1YWwgaGFwcHkgbW9tZW50cy4NCg0KYGBge3Igc2hpbnkgVUksIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQp1aSA8LSBuYXZiYXJQYWdlKCJXaGF0IG1ha2VzIHBlb3BsZSBoYXBweT8iLA0KICAgICAgICAgICAgICAgICB0YWJQYW5lbCgiT3ZlcnZpZXciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGVQYW5lbChoMSgiTW9zdCBGcmVxdWVudCBPY2N1cnJlbmNlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAiY2VudGVyIikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2lkZWJhckxheW91dCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWRlYmFyUGFuZWwoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbGlkZXJJbnB1dChpbnB1dElkID0gInRvcFdvcmRjbG91ZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJOdW1iZXIgb2YgdGVybXMgZm9yIHdvcmQgY2xvdWQ6IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXggPSAxMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IDUwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicigpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVja2JveElucHV0KGlucHV0SWQgPSAidG9wRnJlcUIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJQbG90IEJhciBDaGFydCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gRiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbGlkZXJJbnB1dChpbnB1dElkID0gInRvcEJhcmNoYXJ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiB0ZXJtcyBmb3IgYmFyIGNoYXJ0OiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4gPSAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ID0gMjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IDEwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicigpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVja2JveElucHV0KGlucHV0SWQgPSAidG9wRnJlcU4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJQbG90IE5ldHdvcmsgR3JhcGgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9IEYpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJ0b3BOZXR3b3JrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiBlZGdlcyBmb3IgbmV0d29yayBncmFwaDoiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heCA9IDE1MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gNTApDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluUGFuZWwoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3b3JkY2xvdWQyT3V0cHV0KG91dHB1dElkID0gIldDIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90T3V0cHV0KG91dHB1dElkID0gImZpZ3VyZSIpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICB0YWJQYW5lbCgiSW5kaXZpZHVhbCBUZXJtcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZVBhbmVsKGgxKCJDb21wYXJpc29uIG9mIFByb3BvcnRpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbGlnbiA9ICJjZW50ZXIiKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzaWRlYmFyTGF5b3V0KA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZGViYXJQYW5lbCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdElucHV0KGlucHV0SWQgPSAiYXR0cmlidXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIlNlbGVjdCB0aGUgYXR0cmlidXRlOiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaG9pY2VzID0gYygiR2VuZGVyIiA9ICJnZW5kZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1hcml0YWwgU3RhdHVzIiA9ICJtYXJpdGFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXJlbnRob29kIiA9ICJwYXJlbnRob29kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWZsZWN0aW9uIFBlcmlvZCIgPSAicmVmbGVjdGlvbl9wZXJpb2QiKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpblBhbmVsKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJzY2F0dGVyIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgIHRhYlBhbmVsKCJQYWlyIG9mIFdvcmRzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlUGFuZWwoaDEoIk1vc3QgRnJlcXVlbnQgQmlncmFtcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAiY2VudGVyIikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2lkZWJhckxheW91dCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWRlYmFyUGFuZWwoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3RJbnB1dChpbnB1dElkID0gImZhY3RvciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJTZWxlY3QgdGhlIGF0dHJpYnV0ZToiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hvaWNlcyA9IGMoIkdlbmRlciIgPSAiZ2VuZGVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYXJpdGFsIFN0YXR1cyIgPSAibWFyaXRhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGFyZW50aG9vZCIgPSAicGFyZW50aG9vZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVmbGVjdGlvbiBQZXJpb2QiID0gInJlZmxlY3Rpb25fcGVyaW9kIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1lcmljSW5wdXQoaW5wdXRJZCA9ICJ0b3BCaWdyYW1zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIk51bWJlciBvZiB0b3AgcGFpcnMgdG8gdmlldzoiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heCA9IDI1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAxMCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW5QYW5lbCgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RPdXRwdXQob3V0cHV0SWQgPSAiYmFyIikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgIHRhYlBhbmVsKCJEYXRhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgRFQ6OmRhdGFUYWJsZU91dHB1dCgidGFibGUiKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICApDQopDQpgYGANCg0KIyMjIERldmVsb3AgdGhlIHNlcnZlciBmb3IgdGhlIFIgU2hpbnkgYXBwDQoNClRoaXMgc2hpbnkgYXBwIHZpc3VhbGl6ZXMgc3VtbWFyeSBvZiBkYXRhIGFuZCBkaXNwbGF5cyB0aGUgZGF0YSB0YWJsZSBpdHNlbGYuDQoNCmBgYHtyIHNoaW55IHNlcnZlciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0LCBzZXNzaW9uKSB7DQogIA0KICBwdDEgPC0gcmVhY3RpdmUoew0KICAgIGlmKCFpbnB1dCR0b3BGcmVxQikgcmV0dXJuKE5VTEwpDQogICAgd29yZF9jb3VudCAlPiUNCiAgICAgIHNsaWNlKDE6aW5wdXQkdG9wQmFyY2hhcnQpICU+JQ0KICAgICAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUNCiAgICAgIGdncGxvdChhZXMod29yZCwgbikpICsNCiAgICAgIGdlb21fY29sKCkgKw0KICAgICAgeGxhYihOVUxMKSArDQogICAgICB5bGFiKCJXb3JkIEZyZXF1ZW5jeSIpKw0KICAgICAgY29vcmRfZmxpcCgpDQogIH0pDQogIA0KICBwdDIgPC0gcmVhY3RpdmUoew0KICAgIGlmKCFpbnB1dCR0b3BGcmVxTikgcmV0dXJuKE5VTEwpDQogICAgYmlncmFtX2dyYXBoIDwtIGJpZ3JhbV9jb3VudHMgJT4lDQogICAgICBzbGljZSgxOmlucHV0JHRvcE5ldHdvcmspICU+JQ0KICAgICAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkNCiAgICANCiAgICBzZXQuc2VlZCgxMjMpDQogICAgDQogICAgeCA8LSBncmlkOjphcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjEsICJpbmNoZXMiKSkNCiAgICANCiAgICBnZ3JhcGgoYmlncmFtX2dyYXBoLCBsYXlvdXQgPSAiZnIiKSArDQogICAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBzaG93LmxlZ2VuZCA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgYXJyb3cgPSB4LCBlbmRfY2FwID0gY2lyY2xlKC4wNSwgJ2luY2hlcycpKSArDQogICAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAic2t5Ymx1ZSIsIHNpemUgPSAzKSArDQogICAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFKSArDQogICAgICB0aGVtZV92b2lkKCkNCiAgfSkNCiAgDQogIG91dHB1dCRXQyA8LSByZW5kZXJXb3JkY2xvdWQyKHsNCiAgICANCiAgICB3b3JkX2NvdW50ICU+JQ0KICAgICAgc2xpY2UoMTppbnB1dCR0b3BXb3JkY2xvdWQpICU+JQ0KICAgICAgd29yZGNsb3VkMihzaXplID0gMC42LA0KICAgICAgICAgICAgICAgICByb3RhdGVSYXRpbyA9IDApDQogICAgDQogIH0pDQogIA0KICBvdXRwdXQkZmlndXJlIDwtIHJlbmRlclBsb3QoaGVpZ2h0ID0gNTAwLCB3aWR0aCA9IDUwMCwgew0KICAgIA0KICAgIHB0bGlzdCA8LSBsaXN0KHB0MSgpLHB0MigpKQ0KICAgIHB0bGlzdCA8LSBwdGxpc3RbIXNhcHBseShwdGxpc3QsIGlzLm51bGwpXQ0KICAgIGlmKGxlbmd0aChwdGxpc3QpPT0wKSByZXR1cm4oTlVMTCkNCiAgICANCiAgICBsYXkgPC0gcmJpbmQoYygxLDEpLA0KICAgICAgICAgICAgICAgICBjKDIsMikpDQogICAgDQogICAgZ3JpZC5hcnJhbmdlKGdyb2JzID0gcHRsaXN0LCBsYXlvdXRfbWF0cml4ID0gbGF5KQ0KICB9KQ0KICANCiAgDQogIA0KICBzZWxlY3RlZEF0dHJpYnV0ZSA8LSByZWFjdGl2ZSh7DQogICAgbGlzdChhdHIgPSBpbnB1dCRhdHRyaWJ1dGUpDQogIH0pDQogIA0KICBvdXRwdXQkc2NhdHRlciA8LSByZW5kZXJQbG90KHsNCiAgICB0ZW1wIDwtIGJhZ19vZl93b3JkcyAlPiUNCiAgICAgIGNvdW50KCEhYXMubmFtZShzZWxlY3RlZEF0dHJpYnV0ZSgpJGF0ciksIHdvcmQpICU+JQ0KICAgICAgZ3JvdXBfYnkoISFhcy5uYW1lKHNlbGVjdGVkQXR0cmlidXRlKCkkYXRyKSkgJT4lDQogICAgICBtdXRhdGUocHJvcG9ydGlvbiA9IG4gLyBzdW0obikpICU+JSANCiAgICAgIHNlbGVjdCgtbikgJT4lIA0KICAgICAgc3ByZWFkKCEhYXMubmFtZShzZWxlY3RlZEF0dHJpYnV0ZSgpJGF0ciksIHByb3BvcnRpb24pDQogICAgDQogICAgICBnZ3Bsb3QodGVtcCwgDQogICAgICAgICAgICAgYWVzX3N0cmluZyh4ID0gY29sbmFtZXModGVtcClbMl0sIHkgPSBjb2xuYW1lcyh0ZW1wKVszXSksDQogICAgICAgICAgICAgY29sb3IgPSBhYnMoY29sbmFtZXModGVtcClbM10gLSBjb2xuYW1lcyh0ZW1wKVsyXSkpICsNCiAgICAgIGdlb21fYWJsaW5lKGNvbG9yID0gImdyYXk0MCIsIGx0eSA9IDIpICsNCiAgICAgIGdlb21faml0dGVyKGFscGhhID0gMC4xLCBzaXplID0gMSwgd2lkdGggPSAwLjMsIGhlaWdodCA9IDAuMykgKw0KICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHdvcmQpLCBjaGVja19vdmVybGFwID0gVFJVRSwgdmp1c3QgPSAxLjUpICsNCiAgICAgIHNjYWxlX3hfbG9nMTAobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSkgKw0KICAgICAgc2NhbGVfeV9sb2cxMChsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArDQogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudChsaW1pdHMgPSBjKDAsIDAuMDAxKSwgbG93ID0gImRhcmtzbGF0ZWdyYXk0IiwgaGlnaCA9ICJncmF5NzUiKSArDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQ0KICB9KQ0KICANCiAgDQogIA0KICBzZWxlY3RlZEJpZ3JhbSA8LSByZWFjdGl2ZSh7DQogICAgbGlzdCh2YXIgPSBpbnB1dCRmYWN0b3IpDQogIH0pDQogIA0KICBvdXRwdXQkYmFyIDwtIHJlbmRlclBsb3Qoew0KICAgIGhtX2JpZ3JhbXMgJT4lDQogICAgICBjb3VudCghIWFzLm5hbWUoc2VsZWN0ZWRCaWdyYW0oKSR2YXIpLCBiaWdyYW0sIHNvcnQgPSBUUlVFKSAlPiUNCiAgICAgIGdyb3VwX2J5KCEhYXMubmFtZShzZWxlY3RlZEJpZ3JhbSgpJHZhcikpICU+JQ0KICAgICAgdG9wX24oaW5wdXQkdG9wQmlncmFtcykgJT4lDQogICAgICB1bmdyb3VwKCkgJT4lDQogICAgICBtdXRhdGUoYmlncmFtID0gcmVvcmRlcihiaWdyYW0sIG4pKSAlPiUNCiAgICAgIGdncGxvdChhZXMoYmlncmFtLCBuLCBmaWxsID0gISFhcy5uYW1lKHNlbGVjdGVkQmlncmFtKCkkdmFyKSkpICsNCiAgICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgICAgIGZhY2V0X3dyYXAoYXMuZm9ybXVsYShwYXN0ZSgifiIsIHNlbGVjdGVkQmlncmFtKCkkdmFyKSksIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgICAgIGNvb3JkX2ZsaXAoKQ0KICB9KQ0KICANCiAgDQogIG91dHB1dCR0YWJsZSA8LSBEVDo6cmVuZGVyRGF0YVRhYmxlKHsNCiAgICBEVDo6ZGF0YXRhYmxlKGhtX2RhdGEpDQogIH0pDQp9DQpgYGANCg0KIyMjIFJ1biB0aGUgUiBTaGlueSBhcHANCg0KYGBge3Igc2hpbnkgYXBwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0Kc2hpbnlBcHAodWksIHNlcnZlcikNCmBgYA0K